package com.tgpms.interfaces.dto;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.tgpms.auth.security.TokenService;
import com.tgpms.common.Result;
import com.tgpms.exception.ExceptionExplain;
import com.tgpms.exception.QueryException;
import com.tgpms.interfaces.Request.HttpClient;
import com.tgpms.interfaces.common.CommonRequestDispose;
import com.tgpms.interfaces.properties.Properties;
import com.tgpms.interfaces.properties.Url;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.message.BasicNameValuePair;
import org.apache.tomcat.util.codec.binary.Base64;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author May
 * <p>
 * 提供对外接口
 * 这里的接口是指的我们对VForm接口的二次封装，主要作用是提供给第三方调用
 */
@Service
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class)
@Slf4j
public class Vform implements VformInterface {

    @Resource
    private TokenService tokenService;

    public static String PAS_PWD = "";

    @Resource
    private Redisson redisson;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 1. VForm 第一个接口： 登陆接口
     *
     * @param username   用户名
     * @param password   密码
     * @param tns        账号所在系统标识
     * @param systemCode 系统代码--标示调用接口系统
     * @return 返回值信息
     */
    @Override
    public Result login(String username, String password, String systemCode) throws UnsupportedEncodingException {
        Result result = new Result();
        RLock lock = redisson.getLock(username + "_" + UUID.randomUUID().toString().replace("-", ""));

        Map<String, Object> params = new HashMap<>(16);
        params.put("username", username);
        params.put("password", URLEncoder.encode(password, "utf-8"));
        params.put("tns", Properties.tns);
        params.put("systemCode", systemCode);

        String data = new HttpClient().Login(Url.LOGIN, params, HttpMethod.POST);
        if (StringUtil.isNullOrEmpty(data)) {
            result.setSuccess(false);
            result.setMsg(ExceptionExplain.EMPTY_BY_DATA.getExplain());
            return result;
        }
        JSONObject parseObject = null;
        parseObject = JSON.parseObject(data);
        if (!parseObject.get("message").toString().toUpperCase().equals("SUCCESS")) {
            result.setSuccess(false);
            result.setMsg(parseObject.get("message").toString().toUpperCase());
            result.setData(parseObject);
            return result;
        }
        try {
            lock.tryLock(5, TimeUnit.SECONDS);
            redisTemplate.delete(username + "_Cookie");
            redisTemplate.opsForList().leftPush(username + "_Cookie", HttpClient.cookiesFull);
        } catch (Exception e) {
            e.printStackTrace();
            throw new QueryException(ExceptionExplain.ERROR_BY_LOCK.getExplain());
        } finally {
            lock.unlock();
        }
        result.setData(parseObject);
        return result;
    }

    /**
     * 2. VForm 第二个接口： 数据插入操作 这里是JSON对象
     *
     * @param progCode 屏幕编码
     * @param data     数据，数据按照指定格式提交
     * @return 返回信息
     */
    @Override
    public Result insert(String progCode, Object data) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("data", JSONObject.toJSONString(data));
        String datas = new HttpClient().post(Url.INSERT, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 3. VForm 第三个接口： 数据批量插入操作. 这里是JSON数组对象
     *
     * @param progCode 屏幕编码 例：D3141C
     * @param data     数据，数据按照指定格式提交
     */
    @Override
    public Result insertBatch(String progCode, Object data) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("data", JSONObject.toJSONString(data));
        String datas = new HttpClient().post(Url.INSERT_ALL, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 4. VForm 第四个接口： 数据修改操作
     *
     * @param progCode            屏幕编码 例：D3161C
     * @param key                 记录的关键字段，用来确定唯一记录
     * @param data                需要更新的字段数据
     * @param dataownermode       dataowner模式（该参数的可选值为"Y"和"N"，Y表示采用新的dataonwer模式，适用于福能投、长江大保护的tgpms环境，N表示原来的dataowner模式，适用于其他tgpms环境）
     * @param needswitchdataowner 是否需要先切换用户的当前dataowner（该参数的可选值为"Y"和"N"，Y表示需要切换，N表示不用切换）
     * @param switchdataowner     需要切换的dataowner
     * @return 返回信息
     */
    @Override
    public Result update(String progCode, String key, String data, String dataownermode, String needswitchdataowner, String switchdataowner) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("key", key);
        params.put("data", data);
        if (StringUtils.isEmpty(switchdataowner)) {
            params.put("dataownermode", null);
            params.put("needswitchdataowner", null);
            params.put("switchdataowner", null);
        } else {
            params.put("dataownermode", dataownermode);
            params.put("needswitchdataowner", needswitchdataowner);
            params.put("switchdataowner", switchdataowner);
        }
        String datas = new HttpClient().post(Url.UPDATE, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 5. VForm 第五个接口： 数据批量修改操作
     *
     * @param progCode            屏幕编码 例：D3161C
     * @param data                需要更新的字段数据
     * @param dataownermode       dataowner模式（该参数的可选值为"Y"和"N"，Y表示采用新的dataonwer模式，适用于福能投、长江大保护的tgpms环境，N表示原来的dataowner模式，适用于其他tgpms环境）
     * @param needswitchdataowner 是否需要先切换用户的当前dataowner（该参数的可选值为"Y"和"N"，Y表示需要切换，N表示不用切换）
     * @param switchdataowner     需要切换的dataowner
     * @return 返回信息
     */
    @Override
    public Result updateBatch(String progCode, JSONArray data, String dataownermode, String needswitchdataowner, String switchdataowner) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("data", JSONObject.toJSONString(data));
        if (StringUtils.isEmpty(switchdataowner)) {
            params.put("dataownermode", null);
            params.put("needswitchdataowner", null);
            params.put("switchdataowner", null);
        } else {
            params.put("dataownermode", dataownermode);
            params.put("needswitchdataowner", needswitchdataowner);
            params.put("switchdataowner", switchdataowner);
        }
        String datas = new HttpClient().post(Url.UPDATE_ALL, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 6. VForm 第六个接口： 数据删除操作
     *
     * @param progCode 屏幕编码 例：D3141C
     * @param key      记录的关键字段，用来确定唯一记录
     * @return 返回信息
     */
    @Override
    public Result delete(String progCode, String key) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("key", key);
        String data = new HttpClient().post(Url.DELETE, params, HttpMethod.POST);
        return dispose.commonDispose(data);
    }

    /**
     * 7. VForm 第七个接口： 数据批量删除操作
     *
     * @param progCode 屏幕编码
     * @param key      记录的关键字段，用来确定唯一记录
     * @return 返回信息
     */
    @Override
    public Result deleteBatch(String progCode, JSONArray key) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("key", JSONObject.toJSONString(key));
        String datas = new HttpClient().post(Url.DELETE_ALL, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 8. VForm 第八个接口： 数据查询操作
     *
     * @param progCode            屏幕编码
     * @param data                查询条件，可为空
     * @param page                页码
     * @param size                页规则
     * @param dataownermode       dataowner模式（该参数的可选值为"Y"和"N"，Y表示采用新的dataonwer模式，适用于福能投、长江大保护的tgpms环境，N表示原来的dataowner模式，适用于其他tgpms环境）
     * @param needswitchdataowner 是否需要先切换用户的当前dataowner（该参数的可选值为"Y"和"N"，Y表示需要切换，N表示不用切换）
     * @param switchdataowner     需要切换的dataowner
     * @return 返回信息
     */
    @Override
    public Result find(String progCode, String data, String page, String size, String dataownermode, String needswitchdataowner, String switchdataowner) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("data", data);
        params.put("page", page);
        params.put("size", size);
        if (StringUtils.isEmpty(switchdataowner)) {
            params.put("dataownermode", null);
            params.put("needswitchdataowner", null);
            params.put("switchdataowner", null);
        } else {
            params.put("dataownermode", dataownermode);
            params.put("needswitchdataowner", needswitchdataowner);
            params.put("switchdataowner", switchdataowner);
        }
        String datas = new HttpClient().post(Url.QUERY, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 9. VForm 第九个接口： 范围查询操作
     *
     * @param progCode            屏幕编码
     * @param data                查询条件，可为空
     * @param page                页码
     * @param size                页规则
     * @param dataownermode       dataowner模式（该参数的可选值为"Y"和"N"，Y表示采用新的dataonwer模式，适用于福能投、长江大保护的tgpms环境，N表示原来的dataowner模式，适用于其他tgpms环境）
     * @param needswitchdataowner 是否需要先切换用户的当前dataowner（该参数的可选值为"Y"和"N"，Y表示需要切换，N表示不用切换）
     * @param switchdataowner     需要切换的dataowner
     * @return 返回信息
     */
    @Override
    public Result queryRange(String progCode, Object data, String page, String size, String dataownermode, String needswitchdataowner, String switchdataowner) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("data", JSONObject.toJSONString(data));
        params.put("page", page);
        params.put("size", size);
        if (StringUtils.isEmpty(switchdataowner)) {
            params.put("dataownermode", null);
            params.put("needswitchdataowner", null);
            params.put("switchdataowner", null);
        } else {
            params.put("dataownermode", dataownermode);
            params.put("needswitchdataowner", needswitchdataowner);
            params.put("switchdataowner", switchdataowner);
        }
        String datas = new HttpClient().post(Url.QUERY_RANGE, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 10. VForm 第十个接口： 数据记录数查询操作
     *
     * @param progCode            屏幕编码
     * @param data                查询条件，可为空
     * @param dataownermode       dataowner模式（该参数的可选值为"Y"和"N"，Y表示采用新的dataonwer模式，适用于福能投、长江大保护的tgpms环境，N表示原来的dataowner模式，适用于其他tgpms环境）
     * @param needswitchdataowner 是否需要先切换用户的当前dataowner（该参数的可选值为"Y"和"N"，Y表示需要切换，N表示不用切换）
     * @param switchdataowner     需要切换的dataowner
     * @return 返回信息
     */
    @Override
    public Result queryTotal(String progCode, String data, String dataownermode, String needswitchdataowner, String switchdataowner) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("data", data);
        if (StringUtils.isEmpty(switchdataowner)) {
            params.put("dataownermode", null);
            params.put("needswitchdataowner", null);
            params.put("switchdataowner", null);
        } else {
            params.put("dataownermode", dataownermode);
            params.put("needswitchdataowner", needswitchdataowner);
            params.put("switchdataowner", switchdataowner);
        }
        String datas = new HttpClient().post(Url.QUERY_TOTAL, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 11. VForm 第十一个接口： 范围记录数查询操作
     *
     * @param progCode            屏幕编码
     * @param data                查询条件，可为空
     * @param dataownermode       dataowner模式（该参数的可选值为"Y"和"N"，Y表示采用新的dataonwer模式，适用于福能投、长江大保护的tgpms环境，N表示原来的dataowner模式，适用于其他tgpms环境）
     * @param needswitchdataowner 是否需要先切换用户的当前dataowner（该参数的可选值为"Y"和"N"，Y表示需要切换，N表示不用切换）
     * @param switchdataowner     需要切换的dataowner
     * @return 返回信息
     */
    @Override
    public Result queryRangeTotal(String progCode, Object data, String dataownermode, String needswitchdataowner, String switchdataowner) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("progCode", progCode);
        params.put("data", JSONObject.toJSONString(data));
        if (StringUtils.isEmpty(switchdataowner)) {
            params.put("dataownermode", null);
            params.put("needswitchdataowner", null);
            params.put("switchdataowner", null);
        } else {
            params.put("dataownermode", dataownermode);
            params.put("needswitchdataowner", needswitchdataowner);
            params.put("switchdataowner", switchdataowner);
        }
        String datas = new HttpClient().post(Url.QUERY_RANGETOTAL, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }
    /**
     * 12.1 VForm 第十二个接口： 特殊查询接口（固定多表联合查询） 操作
     *
     * @param code                固定编码：固定编码，调用方提供脚本后，我们给出约定的编码，脚本中需要通过传参替换的地方用[SQL1]、[SQL2]……依次代替
     * @param param               查询条件，可为空 格式: {"[SQL1]":"AND inv.po_no='lwtest' AND inv.invoice_no='lwtest'","[SQL2]":"AND inv.status='1'"}
     * @param page                页码
     * @param size                页规则
     * @param dataownermode       dataowner模式（该参数的可选值为"Y"和"N"，Y表示采用新的dataonwer模式，适用于福能投、长江大保护的tgpms环境，N表示原来的dataowner模式，适用于其他tgpms环境）
     * @param needswitchdataowner 是否需要先切换用户的当前dataowner（该参数的可选值为"Y"和"N"，Y表示需要切换，N表示不用切换）
     * @param switchdataowner     需要切换的dataowner
     * @return 返回值
     */
    @Override
    public Result multiForMsQueryBySql(String code, String param, String page, String size, String dataownermode, String needswitchdataowner, String switchdataowner) {
    	ArrayList<BasicNameValuePair> data = new ArrayList<BasicNameValuePair>();
    	data.add(new BasicNameValuePair("code", code));
    	data.add(new BasicNameValuePair("param", param));
    	data.add(new BasicNameValuePair("page", page));
    	data.add(new BasicNameValuePair("size", size));
    	data.add(new BasicNameValuePair("dataownermode", dataownermode));
    	data.add(new BasicNameValuePair("needswitchdataowner", needswitchdataowner));
    	data.add(new BasicNameValuePair("switchdataowner", switchdataowner));
        if (StringUtils.isEmpty(switchdataowner)) {
            data.add(new BasicNameValuePair("dataownermode", null));
            data.add(new BasicNameValuePair("needswitchdataowner", null));
            data.add(new BasicNameValuePair("switchdataowner", null));
        } else {
            data.add(new BasicNameValuePair("dataownermode", dataownermode));
            data.add(new BasicNameValuePair("needswitchdataowner", needswitchdataowner));
            data.add(new BasicNameValuePair("switchdataowner", switchdataowner));
        }
        System.out.println("vform上送参数为"+data.toString());
        CommonRequestDispose dispose = new CommonRequestDispose();
        String res = new HttpClient().post(Url.MULTIFORMSQUERY, data);
        if(res==null) {
        	return Result.ERROR("请求错误");
        }
        return dispose.commonDispose(res);
    }

    /**
     * 13. VForm 第十三个接口： 值列表分页查询接口 操作
     *
     * @param item_name     值列表字段
     * @param data          举例：{"LABEL":"PO_DESC,PO_NO,PROJECT,PO_STATUS","progCode":"D3161OF","PO_STATUS":"3"}
     *                      LABEL表示需要查询的字段，progCode表示当前屏幕，后面附上值列表查询需要的参数和值
     * @param page          页码
     * @param size          页规格
     * @param param         筛选条件，用like匹配。例如：{"ENTITIES.FULL_NAME":"陈"}
     * @param dataownermode 是否启用调用方的dataowner进行查询（该参数的可选值为"Y"和"N"，Y表示用调用方的dataowner进行查询（同时切换当前用户的datawoner），N表示不用切换，用当前的dataowner进行查询）
     * @param dataowner     需要切换并带上条件的dataowner值
     * @return 返回值
     */
    @Override
    public Result getLovPage(String item_name, String data, String page, String size, String param, String dataownermode, String dataowner) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("itemname", item_name);
        params.put("data", data);
        params.put("page", page);
        params.put("size", size);
        params.put("param", param);
        params.put("dataownermode", dataownermode);
        params.put("dataowner", dataowner);
        String datas = new HttpClient().post(Url.GETLOVPAGE, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 14. VForm 第十四个接口： 值列表查询接口 操作
     *
     * @param item_name     值列表字段
     * @param data          举例：{"LABEL":"PO_DESC,PO_NO,PROJECT,PO_STATUS","progCode":"D3161OF","PO_STATUS":"3"}
     *                      LABEL表示需要查询的字段，progCode表示当前屏幕，后面附上值列表查询需要的参数和值
     * @param param         筛选条件，用like匹配。例如：{"ENTITIES.FULL_NAME":"陈"}
     * @param dataownermode 是否启用调用方的dataowner进行查询（该参数的可选值为"Y"和"N"，Y表示用调用方的dataowner进行查询（同时切换当前用户的datawoner），N表示不用切换，用当前的dataowner进行查询）
     * @param dataowner     需要切换并带上条件的dataowner值
     * @return 返回信息
     */
    @Override
    public Result getLov(String item_name, String data, String param, String dataownermode, String dataowner) throws UnsupportedEncodingException {
//        this.login("CUIMINGYI", "q2ke6r3t", "tgpms_cdtest", "HBDXM");
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("itemname", item_name);
//        params.put("data", JSONObject.toJSONString(data));
        params.put("data", data);
        params.put("param", param);
        params.put("dataownermode", dataownermode);
        params.put("dataowner", dataowner);
        String datas = new HttpClient().post(Url.GETLOV, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    /**
     * 解析 MyDesk传递过来的Token
     *
     * @param token     token
     * @param sessionid Jsessionid的值
     * @param systemid  第三方应用在MyDesk中登记的系统别名
     * @param logoutUrl 第三方应用用户登出的方法
     * @return 返回值
     * <p>
     * sc_user, sc_psw, user_id，full_name等	用户绑定的第三方应用账号、密码、用户在MyDesk中的用户信息
     * not_login	验证失败，没有获取到用户信息，此时，需要重新跳转到获取token的地址：http://ip:port/UserAuthenCenter/sso/login?url=xxx/login
     */
    @Override
    public Result parsingMyDeskToken(String token, String sessionid, String systemid, String logoutUrl) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> map = new HashMap<>(16);
        map.put("token", token);
        map.put("sessionid", sessionid);
        map.put("systemid", systemid);
        map.put("logoutUrl", logoutUrl);
        String data = new HttpClient().mydeskLogin(Url.MYDESK_TOKEN, map, HttpMethod.POST);
        System.out.println("解析返回参数=========>>>>>>>>" + data);
        return dispose.commonDispose(data);
    }

    @Override
    public String descrypt(String cipherText) throws Exception {
        try {
            byte[] cipherBytes = Base64.decodeBase64(cipherText);
            SecretKeySpec secretKeySpec = new SecretKeySpec("Ctwsbxmy73BxIsdG".getBytes(), "AES");
            byte[] AES_IV = {(byte) 0x12, (byte) 0x34, (byte) 0x56,
                    (byte) 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD,
                    (byte) 0xEF, (byte) 0x12, (byte) 0x34, (byte) 0x56,
                    (byte) 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD,
                    (byte) 0xEF};
            IvParameterSpec paramSpec = new IvParameterSpec(AES_IV);
            Cipher ecipher;
            ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            ecipher.init(Cipher.DECRYPT_MODE, secretKeySpec, paramSpec);
            byte[] result = ecipher.doFinal(cipherBytes);
            return new String(result);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Result runReport(String param, String reportCode, String desformat) throws Exception {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("param", param);
        params.put("reportCode", reportCode);
        params.put("desformat", desformat);
        String datas = new HttpClient().post(Url.RUNREPORT, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    @Override
    public Result runReportForOld(String param, String reportCode) throws Exception {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("params", param);
        params.put("reportCode", reportCode);
        String datas = new HttpClient().post(Url.OLDRUNREPORT, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

    @Override
    public Result getUserTgpmsBymobile(String phone, String pp) {
        CommonRequestDispose dispose = new CommonRequestDispose();
        Map<String, Object> params = new HashMap<>(16);
        params.put("phone", phone);
        params.put("pp", pp);
        String datas = new HttpClient().post(Url.TGPMS_GET_USERINFO, params, HttpMethod.POST);
        return dispose.commonDispose(datas);
    }

}
